package ext.tianma.loginAuth;  
  
import java.io.File;
import java.io.IOException;  
import java.io.InputStream;  
import java.io.UnsupportedEncodingException;  
import java.net.URLEncoder;  
//import java.util.Base64;  
import java.util.Enumeration;
import java.util.HashMap;  
import java.util.Map;  
import java.util.UUID;  
import java.util.regex.Pattern;  
import javax.servlet.Filter;  
import javax.servlet.FilterChain;  
import javax.servlet.FilterConfig;  
import javax.servlet.ServletException;  
import javax.servlet.ServletRequest;  
import javax.servlet.ServletResponse;  
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
import javax.servlet.http.HttpSession;  

import org.apache.commons.lang.StringUtils;
import org.apache.http.Header;  
import org.keycloak.adapters.KeycloakDeployment;  
import org.keycloak.adapters.KeycloakDeploymentBuilder;  
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.representations.AccessToken;

import wt.method.MethodContext;
import wt.method.RemoteMethodServer;
import wt.session.SessionHelper;
import wt.util.WTException;

import com.fasterxml.jackson.databind.ObjectMapper;  

  
public class CopyOfKeycloakOIDCFilter implements Filter {  
    public static final String SKIP_PATTERN_PARAM = "keycloak.config.skipPattern";  
    public static final String CONFIG_PATH_PARAM = "keycloak.config.path";  
    protected KeycloakDeployment keycloakDeployment;  
    protected Pattern skipPattern;  
    private String configFilePath = "codebase" + File.separator + "loginfilter" + File.separator + "loginfilter.properties";
	
    @Override  
    public void init(final FilterConfig filterConfig) throws ServletException {  
       //读取初始化参数，白名单（无需认证即可访问的资源） 
        String skipPatternDefinition = filterConfig.getInitParameter(SKIP_PATTERN_PARAM);  
        System.out.println("KeycloakOIDCFilter.............skipPatternDefinition:" + skipPatternDefinition);
        if (skipPatternDefinition != null) {  
            skipPattern = Pattern.compile(skipPatternDefinition, Pattern.DOTALL);  
        } 
        System.out.println("KeycloakOIDCFilter.............skipPattern:" + skipPattern);
        //加载client配置信息  
        String path = "/WEB-INF/keycloak.json";  
        String pathParam = filterConfig.getInitParameter(CONFIG_PATH_PARAM);  
        System.out.println("KeycloakOIDCFilter.............pathParam:" + pathParam);
        if (pathParam != null){  
             path = pathParam;  
        }  
        InputStream is = filterConfig.getServletContext().getResourceAsStream(path);  
        keycloakDeployment = createKeycloakDeploymentFrom(is);  
        System.out.println("KeycloakOIDCFilter.............keycloakDeployment:" + keycloakDeployment);
    }  
  
    private KeycloakDeployment createKeycloakDeploymentFrom(InputStream is) {  
        if (is == null) {  
            System.out.println("No adapter configuration. "  + "Keycloak is unconfigured and will deny all requests.");  
            return new KeycloakDeployment();  
        }  
        return KeycloakDeploymentBuilder.build(is);  
    }  
  
  
    @Override  
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {  
    	System.out.println("KeycloakOIDCFilter.............doFilter");
        HttpServletRequest request = (HttpServletRequest) req;  
        HttpServletResponse response = (HttpServletResponse) res;  
        HttpSession session = request.getSession();  
        RefreshableKeycloakSecurityContext context =  (RefreshableKeycloakSecurityContext)request.getAttribute("org.keycloak.KeycloakSecurityContext");  
        String loginName = "";
        String tokenCode = "";
//       String tokenRefreshToken = "";
       AccessToken tokenContext = null;
        if(context != null){
        	tokenCode = context.getTokenString();
//        	tokenRefreshToken = context.getRefreshToken();
        	tokenContext = context.getToken();
//		        String sub = token.getSubject();//用户内码  
//		        System.out.println("LoginFilterkeycloak...........sub:" + sub);  
		        loginName = tokenContext.getPreferredUsername();//登录账号  
		        System.out.println("LoginFilterkeycloak...........loginName:" + loginName);  
	       }
        // 获取用户访问的路径
     	String targetUrl = request.getRequestURL().toString();
     	System.out.println("KeycloakOIDCFilter.............targetUrl:" + request.getRemoteAddr() + ":" + targetUrl);
     	PropertiesUtil ut = new PropertiesUtil(configFilePath);
        String whiteListURL = ut.getValueByKey("whiteListURL");
        System.out.println("KeycloakOIDCFilter.............whiteListURL:" + whiteListURL);
        //白名单URL，直接放
//        if (shouldSkip(request)) {  
//            chain.doFilter(req, res);  
//        }else
        if(isWhiteURL(targetUrl, whiteListURL)){//过滤白名单
        	chain.doFilter(request, response);
		}else if(targetUrl.contains("netmarkets/jsp/login/login.jsp")){//管理员直接登录
			System.out.println("管理员直接登录.................");
			String auth = request.getHeader("Authorization");
			System.out.println("KeycloakOIDCFilter.............auth:" + auth);
			 if(null != auth){
				 RequestWrap newRequest = new RequestWrap(request);
					newRequest.addHeader("Authorization", "Basic "+ BASE64Encoder.encode((auth).getBytes()) );
					newRequest.setRemoteUser(auth);
					chain.doFilter(newRequest, res);
			 }
		}
//        Cookie[] cookies = request.getCookies();
//		if (cookies != null) {
//			for (Cookie cookie : cookies) {
//				String name = cookie.getName();
//				String value = cookie.getValue();
//				System.out.println(".......................cookie...................");
//				System.out.println("name:" + name);
//				System.out.println("value:" + value);
//			}
//		}
		
//		else if(session.getAttribute("access_token_str") == null){  //非白名单，且未登录  
//			String session_state = (String) session.getAttribute("session_state");
//			String session_code = (String) session.getAttribute("code");
//			String code = request.getParameter("code");
//            System.out.println("................取得授权码:"+code);  
//            //如果不存在授权码，则转向keycloak  
//            if(code == null){  
//                String authURL = this.getRedirectURL(request);    
//                System.out.println("转向keycloak认证");     
//                System.out.println(".............authURL:" + authURL);
//                response.sendRedirect(authURL);  
//                return;  
//            }else{  
//                //请求中携带有授权码  
//                String sessioncode = (String)session.getAttribute("code");  
//                if(code.equals(sessioncode)){  
//                    String authURL = this.getRedirectURL(request);    
//                    System.out.println("授权码已使用，重新转向keycloak认证");    
//                    System.out.println(".............authURL:" + authURL);
//                    response.sendRedirect(authURL);  
//                    return;  
//                }  
//                session.setAttribute("code", code);  
//                System.out.println("回跳URL："+request.getRequestURL().toString()+"..............."+request.getQueryString());                     
//                //存在授权码，则根据授权码换取token  
//                String url = keycloakDeployment.getAuthServerBaseUrl()  
//                        + "/realms/"  
//                        + keycloakDeployment.getRealm()  
//                        + "/protocol/openid-connect/token";  
//                 Map<String, String> params = new HashMap<String, String>();  
//                 params.put("code", code);  
//                 params.put("grant_type", "authorization_code");  
//                 params.put("client_id", keycloakDeployment.getResourceName());  
//                 params.put("redirect_uri", getBaseURL(request));  
//                 params.put("client_secret", String.valueOf(keycloakDeployment.getResourceCredentials().get("secret")));  
//                 System.out.println("授权码换取token");  
//                 System.out.println(".............................url:" + url);  
//                 System.out.println(".............................params" + params.toString());  
//                 Map<String,String> requestHeaders = new HashMap<String, String>();  
//                 requestHeaders.put("Content-Type", "application/x-www-form-urlencoded");  
//                 HttpResponse result = HttpClientUtil.httpPostForm(url, params, requestHeaders, "UTF-8");  
//                if (result.getStatusCode() != 200) {  
//                    System.out.println(result.getStatusCode() + "");  
//                    Header[] headers = result.getHeaders();  
//                    for (int i = 0; i < headers.length; i++) {  
//                        System.out.println(headers[i].getName() + "........................"  + headers[i].getValue());  
//                    }  
//                    System.out.println("------------body--------------");  
//                    System.out.println(result.getBody());  
//                    throw new RuntimeException("由授权码换取token出错！"  + result.getStatusCode() + result.getReasonPhrase());  
//                }  
//                 Map token = new ObjectMapper().readValue(result.getBody(), Map.class);  
//                 String refreshTokenString = token.get("refresh_token").toString();  
//                 String tokenString = token.get("access_token").toString();  
//                 System.out.println("--取得token-----");      
//                 System.out.println(tokenString);  
//                 byte[] bytes = BASE64Encoder.decode(tokenString.split("\\.")[1]);  
//                 String json = new String(bytes);
//                 System.out.println(".............................json:" + json);  
//                 Map accessTokenObj = new ObjectMapper().readValue(json, Map.class);  
//                 session.setAttribute("access_token", accessTokenObj);  
//                 session.setAttribute("access_token_str", tokenString);  
//                 session.setAttribute("refresh_token_str", refreshTokenString);  
//                 chain.doFilter(req, res);  
//                 return;  
//            }  
//        }else{  
		else if("".equals(tokenCode)){
		String authURL = this.getRedirectURL(request);    
          System.out.println("转向keycloak认证");     
          System.out.println(".............authURL:" + authURL);
          response.sendRedirect(authURL);  
          return;  
        }else{
        	//已登录，检查token过期时间  
            Long exp = (long) context.getToken().getExpiration();//Long.parseLong(String.valueOf(accessToken.get("exp")));  
            Long now = System.currentTimeMillis()/1000;  
            //已过期或者离过期时间不足5分钟，则刷新token。  
            if(now > exp || (exp - now) < 1*60){  
                System.out.println(".............now="+now+".............exp="+exp);  
                String url = keycloakDeployment.getAuthServerBaseUrl()  
                        + "/realms/"  
                        + keycloakDeployment.getRealm()  
                        + "/protocol/openid-connect/token";  
                 Map<String, String> params = new HashMap<String, String>();  
                 params.put("refresh_token", String.valueOf(session.getAttribute("refresh_token_str")));  
                 params.put("grant_type", "refresh_token");  
                 params.put("client_id", keycloakDeployment.getResourceName());  
                 params.put("client_secret", String.valueOf(keycloakDeployment.getResourceCredentials().get("secret")));  
                 HttpResponse result = HttpClientUtil.httpPostForm(url, params, null, "UTF-8");  
                 if(result.getStatusCode() != 200){  
                     System.out.println("刷新token出错！"+result.getStatusCode()+ result.getReasonPhrase());  
                     String authURL = this.getRedirectURL(request);   
                     System.out.println("---刷新token出错,重新认证----");   
                     System.out.println(authURL);  
                     response.sendRedirect(authURL);  
                     return;  
                 }  
                 chain.doFilter(request, res);  
                 return;  
            }  
        	
        }
            //token正常，请求正常往下传递。
            RequestWrap newRequest = new RequestWrap(request);
			newRequest.addHeader("Authorization", "Basic "+ BASE64Encoder.encode((loginName).getBytes()) );
			newRequest.setRemoteUser(loginName);
			chain.doFilter(newRequest, res);
            return;  
        }  
    private String getRedirectURL(HttpServletRequest request)   
            throws UnsupportedEncodingException{  
        String callbackURL = URLEncoder.encode(getBaseURL(request), "UTF-8"); 
        String authURL = keycloakDeployment.getAuthServerBaseUrl()  
                + "/realms/"  
                + keycloakDeployment.getRealm()  
                + "/protocol/openid-connect/auth?client_id="  
                + keycloakDeployment.getResourceName()  
                + "&state="  
                + UUID.randomUUID().toString()  
                + "&response_type=code"  
                + "&scope=openid"  
                + "&redirect_uri="  
                + callbackURL;  
        return authURL;  
    }  
      
    private static String getBaseURL(HttpServletRequest request) {  
        String url = request.getScheme()   
                + "://"   
                + request.getServerName()  
                + ":"   
                + request.getServerPort()   
                + request.getContextPath();
        return url;  
    }  
  
    private boolean shouldSkip(HttpServletRequest request) {  
        if (skipPattern == null) {  
            return false;  
        }  
        String requestPath = request.getRequestURI().substring(request.getContextPath().length());  
        System.out.println("KeycloakOIDCFilter.............requestPath:" + requestPath);
        return skipPattern.matcher(requestPath).matches();  
    }  
    private boolean isWhiteURL(String currentURL,String whiteURLs) { 
		if(StringUtils.isBlank(whiteURLs)){
			return false;
		}
		String[] whiteListURLs = whiteURLs.split(";;;qqq");
        for (String whiteURL : whiteListURLs) {  
            if (currentURL.matches(whiteURL)) {  
                return true;  
            }  
        }  
        return false;  
    }  
    @Override  
    public void destroy() {  
    }  
}  